package com.inspur.edp.commonmodel.engine.core.data;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.inspur.edp.cef.core.data.EntityDataBase;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.core.data.EntityDataCollection;
import com.inspur.edp.cef.entity.entity.*;
import com.inspur.edp.cef.entity.entity.dynamicProp.DynamicPropSetImpl;
import com.inspur.edp.cef.entity.i18n.MultiLanguageInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.EntityResInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefEntityResInfoImpl;
import com.inspur.edp.commonmodel.engine.api.data.AssociationInfo;
import com.inspur.edp.commonmodel.engine.api.data.IEngineEntityData;
import com.inspur.edp.commonmodel.engine.core.common.CMUtil;
import com.inspur.edp.commonmodel.engine.core.data.serializer.CommEngineDataSerializer;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.das.commonmodel.IObjectCollection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@JsonSerialize(using = CommEngineDataSerializer.class)
public class EngineRootData extends EntityDataBase implements IValuesContainer, Cloneable, IEntityData, IMultiLanguageData, IEngineEntityData {

  @JSONField(serialize = false)
  protected final IGspCommonObject node;
  protected final CefEntityResInfoImpl resInfo;

  private HashMap<String, Object> values;
  private HashMap<String, IEntityDataCollection> childs;

  @Deprecated
  public EngineRootData(IGspCommonObject node) {
    this(node, null);
  }

  public EngineRootData(IGspCommonObject node, CefEntityResInfoImpl resInfo) {
    this.node = node;
    this.resInfo = resInfo;
  }

  @Override
  public HashMap<String, IEntityDataCollection> innerGetChilds() {
    return (HashMap<String, IEntityDataCollection>) getInnerGetChilds().clone();
  }

  protected HashMap<String, IEntityDataCollection> getInnerGetChilds() {
    if (childs == null) {
      IObjectCollection childNodes = node.getContainChildObjects();
      childs = new HashMap<>(childNodes.size(), 1);
      for (IGspCommonObject child : childNodes) {
        EntityResInfo childResInfo = resInfo != null ? resInfo.getChildEntityResInfo(child.getCode()) : null;
        EntityDataCollection collection = childResInfo == null ?
                new EntityDataCollection() : new EntityDataCollection((CefEntityResInfoImpl) childResInfo);
        childs.put(child.getCode(), collection);
      }
    }
    return childs;
  }

  @Override
  public ICefData innerCreateChild(String s) {
    IGspCommonObject childNode = CMUtil.checkNodeExists(node, s, getNodePredicate());
    EntityResInfo childResInfo = resInfo != null ? resInfo.getChildEntityResInfo(childNode.getCode()) : null;
    return new EngineChildData(childNode, (CefEntityResInfoImpl) childResInfo);
  }

  @Override
  public void innerMergeChildData(String s, ArrayList<IEntityData> childData) {
    IEntityDataCollection childDataCollection = getChilds().get(s);
    if (childDataCollection == null) {
      CMUtil.throwNodeNotFound(node, s);
    }
    for (IEntityData data : childData) {
      childDataCollection.add(data);
    }
  }

  @Override
  public Object innerGetValue(String labelId) {
    IGspCommonElement element = CMUtil
        .checkElementExists(node, labelId, getElementPredicate());
    if(element.getObjectType() == GspElementObjectType.DynamicProp) {
      Object rez= getValues().get(labelId);
      if(rez == null) {
         getValues().put(labelId, (rez = new DynamicPropSetImpl()));
      }
      return rez;
    } else if (!element.getIsRefElement()) {
      return getValues().get(labelId);
    } else {
      String parentLabelId = element.getParentAssociation().getBelongElement().getLabelID();
      Object parentValue = getValues().get(parentLabelId);
      if (parentValue == null) {
        return null;
      } else {
        return ((AssociationInfo) parentValue).getValue(labelId);
      }
    }
  }

  private HashMap<String, Object> getValues() {
    if (values == null) {
      values = new HashMap<>(node.getContainElements().size(), 1);
    }
    return values;
  }

  @Override
  protected ICefData innerCopySelf() {
    try {
      EngineRootData result = (EngineRootData) this.clone();
      result.values = CMUtil.cloneForValues(this.values);
      return result;
    } catch (CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }


  @Override
  protected ICefData innerCopy() {
    try {
      EngineRootData result = (EngineRootData) this.clone();
      result.values = CMUtil.cloneForValues(this.values);
      if (childs != null) {
        result.childs = new HashMap<>(childs.size(), 1);
        for (Map.Entry<String, IEntityDataCollection> pair : childs.entrySet()) {
          EntityDataCollection dataCollection = new EntityDataCollection();
          for (IEntityData item : pair.getValue()) {
            dataCollection.add((IEntityData) item.copy());
          }
          result.childs.put(pair.getKey(), dataCollection);
        }
      }
      return result;
    } catch (CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }


  @Override
  protected List<String> innerGetPropertyNames() {
    return CMUtil.buildNodePropertyNames(node, getElementPredicate());
  }

  @Override
  protected void innerSetValue(String s, Object o) {
    CMUtil.checkElementValue(node, s, o, getElementPredicate());
    getValues().put(s, o);
  }

  @Override
  public Object innerCreateValue(String propertyName) {
    GspAssociation gspAssociation = null;
    for(IGspCommonField gspCommonField: node.getContainElements()){
      if(gspCommonField.getLabelID().equals(propertyName) && gspCommonField.getObjectType() == GspElementObjectType.Association){
        gspAssociation = gspCommonField.getChildAssociations().get(0);
      }
    }
    return new AssociationInfo(gspAssociation);
  }

  @Override
  public String getID() {
    Object rez = getValues().get(node.getIDElement().getLabelID());
    if(rez == null)
      return null;
    else if (rez instanceof String)
      return (String) rez;
    else {
      throw new RuntimeException(String
          .format("ID编号名称分别为[%s][%s][%s]上编号为[%s]的节点的主键字段类型不是普通字符串, 当前类型为%s, 请修改Idp表单后重新推送.",
              node.getBelongModel().getID(), node.getBelongModel().getCode(),
              node.getBelongModel().getName(), node.getCode(),
              node.getIDElement().getObjectType()));
    }
  }

  @Override
  public void setID(String s) {
    getValues().put(node.getIDElement().getLabelID(), s);
  }

  //#region virtual
  protected Predicate<IGspCommonField> getElementPredicate() {
    return null;
  }

  protected Predicate<IGspCommonObject> getNodePredicate() {
    return null;
  }

  private Map<String, MultiLanguageInfo> multiLanguageInfos;
  @JsonIgnore
  @Deprecated
  public Map<String, MultiLanguageInfo> getMultiLanguageInfos(){
    if(this.multiLanguageInfos==null){
      this.multiLanguageInfos=new HashMap<>();
    }
    return multiLanguageInfos;
  }

  @Override
  @JSONField(serialize = false)
  public final IGspCommonObject getNode() {
    return node;
  }
  //#endregion
}
